Web Avanzada Arduino/Esp8266



1. Introducción:

 Anteriormente habíamos visto la forma de monitorizar o controlar un elemento desde una Web embebida pero haciendo siempre un refresco de toda la página para actualizar la información. Esta forma no es la mejor manera e incluso puede ser incómoda.
 En este punto, trataremos de refrescar la información que nos interese manteniendo el resto; para esto se usa "JavaScript".

 Estoy adentrándome en este mundo, y como principiante, trataré de ir aprendiendo y explicando.


2. Primer ejemplo AJAX:

 En este ejemplo, se utiliza AJAX "Asynchronous JavaScript And XML". Al pulsar el botón, llamaremos a la función JavaScript, que nos devolverá el estado de un pulsador en Arduino.
 AJAX se usa para crear aplicaciones interactivas con el usuario. Un programa se ejeuta en el cliente (navegador) mientras se mantiene una comunicación asíncrona con el servidor. Mediante este proceso, se pueden hacer cambios en la página sin necesidad de recargarla completamente.


  2.1. Código HTML de la página:

					
<!DOCTYPE html>
<html>
<head>
	<title>MicroEdu Web</title>
	<script>
		function GetSwitchState()
		{
		nocache = "&nocache=" + Math.random() * 1000000;
		var request = new XMLHttpRequest();
		request.onreadystatechange = function()
		{
			if(this.readyState == 4)
			{
				if(this.status == 200)
				{
					if(this.responseText != null)
					{
						document.getElementById("switch_txt").innerHTML = this.responseText;
					}
				}
			}
		}
		request.open("GET", "ajax_switch" + nocache, true);
		request.send(null);
		}
	<script>
</head>
<body>
	<h1>Estado pulsador usando AJAX</h1>
	<p id="switch_txt">Estado pulsador: Desconocido ...</p>
	<button type="button" onclick="GetSwitchState()">Ver Estado Pulsador</button>
</body>
</html>
					
					
					

 Cuando pulsamos sobre el botón "Ver Estado Pulsador", se llama a la función "GetSwitchState()". Esta función es un JavaScript y por eso está entre etiquetas <script> ... </script>

 Nada más entrar en la función, generamos un número aleatorio que asociaremos a la petición "GET". Este número se usa para que cada petición sea diferente. Si no las diferenciásemos, el navegador mostraría el valor que tiene en la "caché" en vez de hacer la solicitud:
"nocache = "&nocache=" + Math.random() * 1000000;"

 Esta es la petición que se lanza:
"request.open("GET", "ajax_switch" + nocache, true);"

 El siguente paso, es crear una variable llamada "request" dónde almacenaremos la respuesta que nos devuelve el servidor. En esta respuesta tendremos unas propiedades que nos indican el estado:

 Hay más códigos que puedes ver aquí.

 Si la respuesta ha sido realizada correctamente, busca en el cuerpo el elemento cuyo id corresponda a la etiqueta, y le cambia el valor por el texto recibido:
"document.getElementById("switch_txt").innerHTML = this.responseText;"


  2.2. Código Arduino:

					
#include 
#include 

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};    //La mac que asignaremos al Wiznet
IPAddress ip(192, 168, 168, 168);                     //La dirección asignada

EthernetServer server(80);    //Puerto de escucha del servidor

String respuestaCliente;      //String que almacenará la petición del cliente

boolean estadoLed = false;    //Variable que almacena el estado del Led



void setup() 
{
  Serial.begin(9600);         //Inicializamos la conexión Serie para ver lo que sucede
  Ethernet.begin(mac, ip);    //Inicializamos la conexión Ethernet
  pinMode(7, INPUT);          //Inicializamos el pin 7  como entrada
  server.begin();             //Levantamos el servidor escuchando en el puerto definido
  Serial.print("server is at ");    //Por puerto Serie, nos indica qué IP tiene 
  Serial.println(Ethernet.localIP());
}


void loop() 
{
  EthernetClient client = server.available();   //Creamos un elemento client
  char c;       //Variable para almacenar caracteres recibidos
  if (client)   //Comprobamos si client ha recibido solicitud de conexión 
  {
    Serial.println("Cliente nuevo");
    boolean currentLineIsBlank = false;  //Variable que nos indicará que hemos recibido línea nueva
    while (client.connected()) 
    {
      while (client.available() > 0)    //Recogemos todos los datos del buffer y los almacenamos en respuestaCliente
      {
        c = client.read();
        respuestaCliente += c;
      }
      
        if (c == '\n' && currentLineIsBlank) 
        {
          //Sacamos por puerto Serie lo recibido
          Serial.println(respuestaCliente);
          
          //Enviamos respuesta HTTP estándar
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close"));  
          client.println();

          //Si en la respuesta detectamos la palabra clave "ajax_switch", no enviamos la pagina
          //enviamos la respuesta a la consulta
          if (respuestaCliente.indexOf("ajax_switch") > -1) 
          { 
            procesar(client);
          }
          else
          {      
          //Enviamos pagina
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<head>"));
          client.println(F("<title> MicroEdu Web</title>"));

          client.println(F("<script>"));
          client.println(F("function GetSwitchState(){nocache = '&nocache=' + Math.random() * 1000000;"));
          client.println(F("var request = new XMLHttpRequest();"));
          client.println(F("request.onreadystatechange = function(){"));
          client.println(F("  if(this.readyState == 4){"));
          client.println(F("if(this.status == 200){"));
          client.println(F("if(this.responseText != null){"));
          client.println(F("document.getElementById('switch_txt').innerHTML = this.responseText;}}}}"));
          client.println(F("request.open('GET', 'ajax_switch' + nocache, true);"));
          client.println(F("request.send(null);}"));
          client.println(F("</script>"));

          client.println(F("</head>"));
          client.println(F("<body> <h1> Estado Pulsador Usando AJAX </h1>"));
          
          client.println(F("<p id='switch_txt'>Estado pulsador: Desconocido ...</p>"));
          client.println(F("<button type='button' onclick='GetSwitchState()'>Ver Estado Pulsador</button>"));
          
          client.println(F("</body></html>"));
          client.println();
          }

          respuestaCliente=""; 
          break;
        }
        
        if (c == '\n') 
        {
          currentLineIsBlank = true;      //Nos preparamos para otra conexion
          Serial.println("Empezamos ...");
        }
        else if (c != '\r') 
        {         
          currentLineIsBlank = false;     //Aun no hemos terminado esta conexion
          Serial.println("No hemos terminado ...");
        }
        client.flush();
      }
    }
    delay(1);
    client.stop();
}




void procesar(EthernetClient client2)
{
    if (digitalRead(7)) 
    {
        client2.println("Switch state: ON");
        Serial.println ("Pulsador activado");
    }
    else 
    {
        client2.println("Switch state: OFF");
        Serial.println ("Pulsador desactivado");
    }
}
  					
					
					
					

  2.3. Resultado:




3. Segundo ejemplo AJAX:

 Este ejemplo es igual al anterior, pero se refresca el estado automáticamente sin necesidad de un botón.

  3.1. Código HTML de la página:

					
<!DOCTYPE html>
<html>
<head>
	<title>MicroEdu Web</title>
	<script>
		function GetSwitchState()
		{
		nocache = "&nocache=" + Math.random() * 1000000;
		var request = new XMLHttpRequest();
		request.onreadystatechange = function()
		{
			if(this.readyState == 4)
			{
				if(this.status == 200)
				{
					if(this.responseText != null)
					{
						document.getElementById("switch_txt").innerHTML = this.responseText;
					}
				}
			}
		}
		request.open("GET", "ajax_switch" + nocache, true);
		request.send(null);
		setTimeout('GetSwitchState()', 1000);
		}
	<script>
</head>
<body onload="GetSwitchState()">
	<h1>Estado pulsador usando AJAX</h1>
	<p id="switch_txt">Estado pulsador: Desconocido ...</p>
</body>
</html>
					
					
					

 En la función script, añadimos un línea que indica que debe ejecutarse cada segundo esta función:
"setTimeout('GetSwitchState()', 1000);"

 Luego, le pasamos el parámetro "onload" al cuerpo de la página. Al hacer esto, una vez que termina de cargarse la página, llama a la función indicada:
"<body onload="GetSwitchState()">"

Nota: Parece que cada vez que actualiza el texto, identifica como que termina de cargar y vuelve a llamar a la función. Hace esto continuamente; por eso se refresca cada segundo.

  3.2. Código Arduino:

					
#include 
#include 

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};    //La mac que asignaremos al Wiznet
IPAddress ip(192, 168, 168, 168);                     //La dirección asignada

EthernetServer server(80);    //Puerto de escucha del servidor

String respuestaCliente;      //String que almacenará la petición del cliente

boolean estadoLed = false;    //Variable que almacena el estado del Led



void setup() 
{
  Serial.begin(9600);         //Inicializamos la conexión Serie para ver lo que sucede
  Ethernet.begin(mac, ip);    //Inicializamos la conexión Ethernet
  pinMode(7, INPUT);          //Inicializamos el pin 7  como entrada
  server.begin();             //Levantamos el servidor escuchando en el puerto definido
  Serial.print("server is at ");    //Por puerto Serie, nos indica qué IP tiene 
  Serial.println(Ethernet.localIP());
}


void loop() 
{
  EthernetClient client = server.available();   //Creamos un elemento client
  char c;       //Variable para almacenar caracteres recibidos
  if (client)   //Comprobamos si client ha recibido solicitud de conexión 
  {
    Serial.println("Cliente nuevo");
    boolean currentLineIsBlank = false;  //Variable que nos indicará que hemos recibido línea nueva
    while (client.connected()) 
    {
      while (client.available() > 0)    //Recogemos todos los datos del buffer y los almacenamos en respuestaCliente
      {
        c = client.read();
        respuestaCliente += c;
      }
      
        if (c == '\n' && currentLineIsBlank) 
        {
          //Sacamos por puerto Serie lo recibido
          Serial.println(respuestaCliente);
          
          //Enviamos respuesta HTTP estándar
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close"));  
          client.println();

          //Si en la respuesta detectamos la palabra clave "ajax_switch", no enviamos la pagina
          //enviamos la respuesta a la consulta
          if (respuestaCliente.indexOf("ajax_switch") > -1) 
          { 
            procesar(client);
          }
          else
          {      
          //Enviamos pagina
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<head>"));
          client.println(F("<title> MicroEdu Web</title>"));

          client.println(F("<script>"));
          client.println(F("function GetSwitchState(){nocache = '&nocache=' + Math.random() * 1000000;"));
          client.println(F("var request = new XMLHttpRequest();"));
          client.println(F("request.onreadystatechange = function(){"));
          client.println(F("  if(this.readyState == 4){"));
          client.println(F("if(this.status == 200){"));
          client.println(F("if(this.responseText != null){"));
          client.println(F("document.getElementById('switch_txt').innerHTML = this.responseText;}}}}"));
          client.println(F("request.open('GET', 'ajax_switch' + nocache, true);"));
          client.println(F("request.send(null);"));
		  client.println(F("setTimeout('GetSwitchState()', 1000);}"));
          client.println(F("</script>"));

          client.println(F("</head>"));
		  client.println(F("<body onload='GetSwitchState()'> <h1> Estado Pulsador Usando AJAX </h1>"));
          
          client.println(F("<p id='switch_txt'>Estado pulsador: Desconocido ...</p>"));
          
          client.println(F("</body></html>"));
          client.println();
          }

          respuestaCliente=""; 
          break;
        }
        
        if (c == '\n') 
        {
          currentLineIsBlank = true;      //Nos preparamos para otra conexion
          Serial.println("Empezamos ...");
        }
        else if (c != '\r') 
        {         
          currentLineIsBlank = false;     //Aun no hemos terminado esta conexion
          Serial.println("No hemos terminado ...");
        }
        client.flush();
      }
    }
    delay(1);
    client.stop();
}




void procesar(EthernetClient client2)
{
    if (digitalRead(7)) 
    {
        client2.println("Switch state: ON");
        Serial.println ("Pulsador activado");
    }
    else 
    {
        client2.println("Switch state: OFF");
        Serial.println ("Pulsador desactivado");
    }
}
  					
					
					
					

  3.3. Resultado:




4. Empezando con XML:

 En este ejemplo, el Arduino devuelve el estado de las entradas usando un fichero o estructura XML y no un bloque de HTML.
 El XML se usa para representar información estructurada en la Web y esto facilita que los valores sean fácilmente extraíbles por JavaScript sin necesidad de añadir código para interpretarlos.

 Tiene la siguiente estructura:



 Los nombres de las variables pueden ser repetidos o diferentes. Si son repetidos, se accede mediante un índice:



 En el siguiente ejemplo usa nombres diferentes:



 En este caso se acceden mediante el nombre e índice 0:




 En el siguiente ejemplo, se pasará una estructura XML, de la que se extraerá el valor de la variable que se necesite.


  4.1. Código HTML:


<!DOCTYPE html>
<html>
<head>
	<title>MicroEdu Web</title>
	<script>
		function GetSwitchState()
		{
		nocache = "&nocache=" + Math.random() * 1000000;
		var request = new XMLHttpRequest();
		request.onreadystatechange = function()
		{
			if(this.readyState == 4)
			{
				if(this.status == 200)
				{
					if(this.responseText != null)
					{
						document.getElementById("contador_1").innerHTML = 
						this.responseXML.getElementsByTagName('cont_1')[0].chiNodes[0].nodeValue;
						document.getElementById("contador_2").innerHTML = 
						this.responseXML.getElementsByTagName('cont_2')[0].chiNodes[0].nodeValue;
						document.getElementById("pulsador_1").innerHTML = 
						this.responseXML.getElementsByTagName('puls_1')[0].chiNodes[0].nodeValue;
					}
				}
			}
		}
		request.open("GET", "ajax_switch" + nocache, true);
		request.send(null);
		setTimeout('GetArduinoInputs()', 1000);
		}
	</script>
</head>
<body onload="GetArduinoInputs()">
	<h1>Estado contadores y pulsador usando AJAX y XML</h1>
	<p>Estado Pulsador: <span id="pulsador_1">...</span></p>
	<p>Valor Contador 1: <span id="contador_1">...</span></p>
	<p>Valor Contador 2: <span id="contador_2">...</span></p>
</body>
</html>
					
					

 El código en Arduino:


#include 
#include 

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};    //La mac que asignaremos al Wiznet
IPAddress ip(192, 168, 168, 168);                     //La dirección asignada

EthernetServer server(80);    //Puerto de escucha del servidor

String respuestaCliente;      //String que almacenará la petición del cliente

boolean estadoLed = false;    //Variable que almacena el estado del Led

int contador_1, contador_2;   //Variables de contador



void setup() 
{
  Serial.begin(9600);         //Inicializamos la conexión Serie para ver lo que sucede
  Ethernet.begin(mac, ip);    //Inicializamos la conexión Ethernet
  pinMode(7, INPUT);          //Inicializamos el pin 7  como entrada
  server.begin();             //Levantamos el servidor escuchando en el puerto definido
  Serial.print("server is at ");    //Por puerto Serie, nos indica qué IP tiene 
  Serial.println(Ethernet.localIP());
}


void loop() 
{
  EthernetClient client = server.available();   //Creamos un elemento client
  char c;       //Variable para almacenar caracteres recibidos
  if (client)   //Comprobamos si client ha recibido solicitud de conexión 
  {
    Serial.println("Cliente nuevo");
    boolean currentLineIsBlank = false;  //Variable que nos indicará que hemos recibido línea nueva
    while (client.connected()) 
    {
      while (client.available() > 0)    //Recogemos todos los datos del buffer y los almacenamos en respuestaCliente
      {
        c = client.read();
        respuestaCliente += c;
      }
      
        if (c == '\n' && currentLineIsBlank) 
        {
          //Sacamos por puerto Serie lo recibido
          Serial.println(respuestaCliente);

          //Cabecera estandar de respuesta
          client.println(F("HTTP/1.1 200 OK"));
          

          //Si en la respuesta detectamos la palabra clave "ajax_switch", no enviamos la pagina
          //enviamos la respuesta a la consulta
          if (respuestaCliente.indexOf("xml_values") > -1) 
          { 
            //Enviamos respuesta XML estándar
            client.println(F("Content-Type: text/xml"));
            client.println(F("Connection: keep-alive"));  
            client.println();
          
            procesar(client);
          }
          else
          {  
          //Enviamos respuesta HTTP estándar
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: keep-alive"));  
          client.println();
              
          //Enviamos pagina
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<head>"));
          client.println(F("<title> MicroEdu Web</title>"));

          client.println(F("<script>"));
          client.println(F("function GetArduinoValues(){nocache = '&nocache=' + Math.random() * 1000000;"));
          client.println(F("var request = new XMLHttpRequest();"));
          client.println(F("request.onreadystatechange = function(){"));
          client.println(F("  if(this.readyState == 4){"));
          client.println(F("if(this.status == 200){"));
          client.println(F("if(this.responseText != null){"));

          //client.println(F("document.getElementById('contador_1').innerHTML = this.responseXML.getElementsByTagName('cont1')[0].childNodes[0].nodeValue;}}}}"));
                                
          client.println(F("document.getElementById('contador1').innerHTML = this.responseXML.getElementsByTagName('cont1')[0].childNodes[0].nodeValue;"));
          client.println(F("document.getElementById('contador2').innerHTML = this.responseXML.getElementsByTagName('cont2')[0].childNodes[0].nodeValue;"));
          client.println(F("document.getElementById('pulsador1').innerHTML = this.responseXML.getElementsByTagName('puls1')[0].childNodes[0].nodeValue;}}}}"));
          
          client.println(F("request.open('GET', 'xml_values' + nocache, true);"));
          client.println(F("request.send(null);"));
          client.println(F("setTimeout('GetArduinoValues()', 1000);}"));
          client.println(F("</script>"));

          client.println(F("</head>"));
          client.println(F("<body onload = 'GetArduinoValues()'> <h1> Estado contadores y pulsador usando AJAX y XML </h1>"));
          
          client.println(F("<p>Estado Pulsador: <span id='pulsador1'>...</span></p>"));
          client.println(F("<p>Estado Contador 1: <span id='contador1'>...</span></p>"));
          client.println(F("<p>Estado Contador 2: <span id='contador2'>...</span></p>"));


          
          client.println(F("</body></html>"));
          client.println();
          }

          respuestaCliente=""; 
          break;
        }
        
        if (c == '\n') 
        {
          currentLineIsBlank = true;      //Nos preparamos para otra conexion
          Serial.println("Empezamos ...");
        }
        else if (c != '\r') 
        {         
          currentLineIsBlank = false;     //Aun no hemos terminado esta conexion
          Serial.println("No hemos terminado ...");
        }
        client.flush();
      }
    }
    delay(1);
    client.stop();
}






void procesar(EthernetClient client2)
{
  client2.println("");
  client2.println("");
  
  client2.print("");
    if (digitalRead(7)) 
    {
        client2.print("Switch state: ON");
        Serial.print ("Pulsador activado");
    }
    else 
    {
        client2.print("Switch state: OFF");
        Serial.print ("Pulsador desactivado");
    }
  client2.println("");

  client2.print("");
    if (contador_1 < 200) 
      contador_1++;
    else
      contador_1 = 0;
  client2.print (contador_1);
  client2.println("");

  client2.print("");
    if (contador_2 < 200) 
      contador_2 = contador_2 + 5;
    else
      contador_2 = 0;
  client2.print(contador_2);
  client2.println("");

  client2.println("");
}
  
					
					

 El resultado:




5. Descargas:



www.microedu.es

Si chove, non orballa!